﻿#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using PHMEE.CommonUtils;
using PHMEE.Localization;
using Volo.Abp;

namespace PHMEE.EnergyEfficiency;

public class CIIAppService : PHMEEAppService, ICIIAppService
{
    private readonly IGpsTdRepository _gpsTdRepository;
    private readonly IPHMEETimeProvider _phmeeTimeProvider;
    private readonly FuelConsumptionCalculator _fuelConsumptionCalculator;
    private readonly VesselEeConfigDomainService _vesselEeConfigDomainService;
    private readonly ILogAbstractRepository _logAbstractRepository;
    private readonly ICiiReductionFactorRepository _ciiReductionFactorRepository;
    private readonly LogAbstractDomainService _logAbstractDomainService;

    public CIIAppService(
        IGpsTdRepository gpsTdRepository,
        IPHMEETimeProvider phmeeTimeProvider,
        FuelConsumptionCalculator fuelConsumptionCalculator,
        VesselEeConfigDomainService vesselEeConfigDomainService,
        ILogAbstractRepository logAbstractRepository,
        ICiiReductionFactorRepository ciiReductionFactorRepository,
        LogAbstractDomainService logAbstractDomainService)
    {
        LocalizationResource = typeof(EnergyEfficiencyResource);
        
        _gpsTdRepository = gpsTdRepository;
        _phmeeTimeProvider = phmeeTimeProvider;
        _fuelConsumptionCalculator = fuelConsumptionCalculator;
        _vesselEeConfigDomainService = vesselEeConfigDomainService;
        _logAbstractRepository = logAbstractRepository;
        _ciiReductionFactorRepository = ciiReductionFactorRepository;
        _logAbstractDomainService = logAbstractDomainService;
    }

    /// <inheritdoc />
    public async Task<AnnualCIIDto> AnnualCIIUnadjusted(Guid vesselId, int? year)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        var (yearNumber, startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForYearQuery(year, utcNow);

        var z = await _ciiReductionFactorRepository.GetByYearAsync(yearNumber);
        if (z is null)
        {
            return new AnnualCIIDto(null, null, null);
        }

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new AnnualCIIDto(null, null, null);
        }

        var ciiUnadjustedWithGrade = await CIIUnadjusted(vesselId, startDateTime, endDateTime, vesselEeConfig, z);

        var requiredCII = CIIService.RequiredCII(vesselEeConfig.VesselType, vesselEeConfig.DWT, vesselEeConfig.GT, z.Z);

        return new AnnualCIIDto(ciiUnadjustedWithGrade.CII, ciiUnadjustedWithGrade.Grade, requiredCII);
    }

    /// <summary>
    /// 算“年度 CII（未修正）”、“本年度 CII 预测”、“本年度累计 CII” 的通用方法
    /// </summary>
    /// <param name="vesselId"></param>
    /// <param name="startTime"></param>
    /// <param name="endTime"></param>
    /// <param name="vesselEeConfig"></param>
    /// <param name="z"></param>
    /// <returns></returns>
    private async Task<CIIWithGradeDto> CIIUnadjusted(Guid vesselId, DateTime startTime,
        DateTime endTime, VesselEeConfig vesselEeConfig, CiiReductionFactor z)
    {
        var capacity = VesselEeConfigDomainService.GetCapacity(vesselEeConfig.VesselType, vesselEeConfig.DWT,
            vesselEeConfig.GT);

        var logAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startTime, endTime);

        var cii = LogAbstractFormulas.CIINullable(logAbstracts, capacity, vesselEeConfig.FuelTypes);

        CIIGrade? ciiGrade = cii is null
            ? null
            : CIIService.DetermineCIIGrade(cii.Value, vesselEeConfig.VesselType, vesselEeConfig.DWT, vesselEeConfig.GT,
                z.Z);

        return new CIIWithGradeDto(cii, ciiGrade);
    }

    /// <inheritdoc />
    public async Task<AnnualCIIDto> AnnualCIIAdjusted(Guid vesselId, int? year)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();
        
        var (yearNumber, startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForYearQuery(year, utcNow);

        var z = await _ciiReductionFactorRepository.GetByYearAsync(yearNumber);
        if (z is null)
        {
            return new AnnualCIIDto(null, null, null);
        }

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new AnnualCIIDto(null, null, null);
        }

        var capacity = VesselEeConfigDomainService.GetCapacity(vesselEeConfig.VesselType, vesselEeConfig.DWT,
            vesselEeConfig.GT);

        var logAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDateTime, endDateTime);

        // 航次
        var voyages = await _logAbstractDomainService.GetVoyagesAsync(logAbstracts, vesselId);

        // 调整 log abstract
        var adjustedLogAbstracts =
            await _logAbstractDomainService.GetAdjustedLogAbstractsByTimeRangeAsync(logAbstracts, vesselId);

        // sts log abstracts
        // var stsLogAbstracts = LogAbstractDomainService.GetStsLogAbstracts(voyages);
        var stsLogAbstracts = voyages.GetSTSLogAbstracts();

        var yi = LogAbstractFormulas.GetYi(yearNumber);

        var cii = LogAbstractFormulas.CIICorrectNullable(logAbstracts, adjustedLogAbstracts, stsLogAbstracts, yi,
            capacity, vesselEeConfig.Fi, vesselEeConfig.Fm, vesselEeConfig.Fc, vesselEeConfig.FiVSE,
            vesselEeConfig.FtTanker, vesselEeConfig.FuelTypes);

        CIIGrade? ciiGrade = cii is null
            ? null
            : CIIService.DetermineCIIGrade(cii.Value, vesselEeConfig.VesselType, vesselEeConfig.DWT, vesselEeConfig.GT,
                z.Z);

        var requiredCII = CIIService.RequiredCII(vesselEeConfig.VesselType, vesselEeConfig.DWT, vesselEeConfig.GT, z.Z);

        return new AnnualCIIDto(cii, ciiGrade, requiredCII);
    }

    /// <inheritdoc />
    public async Task<CIIWithGradeDto> CurrentVoyageCII(Guid vesselId)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new CIIWithGradeDto(null, null);
        }

        var z = await _ciiReductionFactorRepository.GetByYearAsync(utcNow.Year);
        if (z is null)
        {
            return new CIIWithGradeDto(null, null);
        }

        // 获取当前航次的所有 log abstracts
        var currentVoyageLogAbstracts = await _logAbstractDomainService.GetCurrentVoyageLogAbstracts(vesselId, utcNow);

        if (currentVoyageLogAbstracts.Count == 0)
        {
            // 当前并不在任何航次内
            return new CIIWithGradeDto(null, null);
        }

        var capacity = VesselEeConfigDomainService.GetCapacity(vesselEeConfig.VesselType, vesselEeConfig.DWT,
            vesselEeConfig.GT);

        var cii = LogAbstractFormulas.CIINullable(currentVoyageLogAbstracts, capacity, vesselEeConfig.FuelTypes);

        CIIGrade? ciiGrade = cii is null
            ? null
            : CIIService.DetermineCIIGrade(cii.Value, vesselEeConfig.VesselType, vesselEeConfig.DWT, vesselEeConfig.GT,
                z.Z);

        return new CIIWithGradeDto(cii, ciiGrade);
    }

    /// <inheritdoc />
    public async Task<CIIWithGradeDto> InstantaneityCII(Guid vesselId)
    {
        var endTime = _phmeeTimeProvider.GetUtcNow();

        // 瞬时的范围扩展为 30 分钟，防止岸端因为数据同步延迟而没有数据
        var startTime = endTime.AddMinutes(-30);

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new CIIWithGradeDto(null, null);
        }

        var z = await _ciiReductionFactorRepository.GetByYearAsync(endTime.Year);
        if (z is null)
        {
            return new CIIWithGradeDto(null, null);
        }

        var capacity = VesselEeConfigDomainService.GetCapacity(vesselEeConfig.VesselType, vesselEeConfig.DWT,
            vesselEeConfig.GT);
                
        var distance = (await _gpsTdRepository.GetDistancesCloseOpenAsync(vesselId, startTime, endTime)).Sum();

        var qmjship = new float[vesselEeConfig.FuelTypes.Count];
        var cfj = new float[vesselEeConfig.FuelTypes.Count];

        var qmjshipDict =
            await _fuelConsumptionCalculator.GetFuelConsumptionForDateTimePeriodAsync(vesselId, startTime, endTime);

        for (int i = 0; i < vesselEeConfig.FuelTypes.Count; i++)
        {
            var fuelType = vesselEeConfig.FuelTypes[i];
            cfj[i] = FuelCfMapper.GetFuelCfCO2(fuelType);
            qmjship[i] = qmjshipDict.GetValueOrDefault(fuelType);
        }

        float? ciiUnadjusted = CIIFormulas.InstantaneityCIIUnAdjusted(qmjship, cfj,
            capacity, distance);

        if (ciiUnadjusted is null)
        {
            return new CIIWithGradeDto(null, null);
        }

        var ciiGrade = CIIService.DetermineCIIGrade(ciiUnadjusted.Value, vesselEeConfig.VesselType, vesselEeConfig.DWT,
            vesselEeConfig.GT, z.Z);

        return new CIIWithGradeDto(ciiUnadjusted, ciiGrade);
    }

    /// <inheritdoc />
    public async Task<CIIWithGradeDto> CurrentYearCIIPrediction(Guid vesselId)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();
        var yearNumber = utcNow.Year;
        var startDate = QueryDateTimeUtils.GetUtcYearStartDate(utcNow.Year);

        var z = await _ciiReductionFactorRepository.GetByYearAsync(yearNumber);
        if (z is null)
        {
            return new CIIWithGradeDto(null, null);
        }

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new CIIWithGradeDto(null, null);
        }

        return await CIIUnadjusted(vesselId, startDate, utcNow, vesselEeConfig, z);
    }

    /// <inheritdoc />
    public async Task<CIIWithGradeDto> CurrentYearCumulativeCII(Guid vesselId)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();
        var startDate = QueryDateTimeUtils.GetUtcYearStartDate(utcNow.Year);
        var yearNumber = utcNow.Year;

        var z = await _ciiReductionFactorRepository.GetByYearAsync(yearNumber);
        if (z is null)
        {
            return new CIIWithGradeDto(null, null);
        }

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new CIIWithGradeDto(null, null);
        }

        return await CIIUnadjusted(vesselId, startDate, utcNow, vesselEeConfig, z);
    }

    /// <inheritdoc />
    public async Task<CIIAndVDto> ExpectedPerformanceToMeetCIICurrentYear(Guid vesselId,
        CIIGrade targetCIIGrade)
    {
        if (targetCIIGrade == CIIGrade.E)
        {
            throw new UserFriendlyException(L[PHMEEEnergyEfficiencyErrorCodes.TargetCIIGradeCannotBeE],
                PHMEEEnergyEfficiencyErrorCodes.TargetCIIGradeCannotBeE);
        }

        DateTime today = _phmeeTimeProvider.GetUtcToday();
        var startDate = QueryDateTimeUtils.GetUtcYearStartDate(today.Year);
        var endDate = today;

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new CIIAndVDto(null, null, null, L[PHMEEEnergyEfficiencyErrorCodes.VesselConfigNotFound]);
        }

        var z = await _ciiReductionFactorRepository.GetByYearAsync(today.Year);
        if (z is null)
        {
            return new CIIAndVDto(null, null, null,
                L[PHMEEEnergyEfficiencyErrorCodes.CIIReductionFactorNotFoundWithoutYearNumber]);
        }

        var capacity = VesselEeConfigDomainService.GetCapacity(vesselEeConfig.VesselType, vesselEeConfig.DWT,
            vesselEeConfig.GT);

        // 今年已经航行的 log abstract
        var finishedLogAbstracts = await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDate, endDate);

        // 已经航行的天数的平均 CII
        var ciiFinished = LogAbstractFormulas.CIINullable(finishedLogAbstracts, capacity, vesselEeConfig.FuelTypes);
        if (ciiFinished is null)
        {
            return new CIIAndVDto(null, null, null, L[PHMEEEnergyEfficiencyErrorCodes.CannotCalculateAverageCIIWithSailingDays]);
        }

        // 要求的年度运营 CII
        var requiredCII = CIIService.RequiredCII(vesselEeConfig.VesselType, vesselEeConfig.DWT, vesselEeConfig.GT, z.Z);

        // 年度期望的 CII 等级（targetCIIGrade）的评级边界
        var ciiMax = CIIService.GetCIIGradeBoundary(requiredCII, targetCIIGrade, vesselEeConfig.VesselType,
            vesselEeConfig.DWT);

        // 剩余年度要达到的 CII
        var ciiPre = LogAbstractFormulas.OptimizeCIISuggestionNullable(today, startDate, ciiFinished.Value, ciiMax);
        if (ciiPre is null)
        {
            return new CIIAndVDto(null, null, null, L[PHMEEEnergyEfficiencyErrorCodes.CannotCalculateRemainingCII]);
        }

        // // 剩余年度要达到的 CII 的等级
        // var ciiPreGrade = _ciiService.DetermineCIIGrade(ciiPre.Value, vesselEeConfig.VesselType, vesselEeConfig.DWT, vesselEeConfig.GT, z.Z);

        var vSug = vesselEeConfig.VToCIIs.FindClosestVByCII(ciiPre.Value);
        if (vSug is null)
        {
            return new CIIAndVDto(null, null, null, L[PHMEEEnergyEfficiencyErrorCodes.CannotProvideValidSpeedAdvice]);
        }

        // 根据邱工要求，<0.1 的数值，都显示 0.1
        if (ciiPre < 0.1f)
        {
            ciiPre = 0.1f;
        }

        return new CIIAndVDto(ciiPre, targetCIIGrade, vSug, null);
    }

    /// <inheritdoc />
    public async Task<CIIAndVDto> EstimatedCIITargetVoyage(Guid vesselId, float targetDistance, float hours)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();
        if (hours <= 0)
        {
            throw new UserFriendlyException(L[PHMEEEnergyEfficiencyErrorCodes.ValueMustBeGreaterThanZero],
                PHMEEEnergyEfficiencyErrorCodes.ValueMustBeGreaterThanZero);
        }

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new CIIAndVDto(null, null, null, L[PHMEEEnergyEfficiencyErrorCodes.VesselConfigNotFound]);
        }

        var z = await _ciiReductionFactorRepository.GetByYearAsync(utcNow.Year);
        if (z is null)
        {
            return new CIIAndVDto(null, null, null,
                L[PHMEEEnergyEfficiencyErrorCodes.CIIReductionFactorNotFoundWithoutYearNumber]);
        }

        // 根据 targetDistance 和 hours 计算航速
        var vg = targetDistance / hours;

        // 根据航速查找对应的 CII
        var predictCII = vesselEeConfig.VToCIIs.FindClosestCIIByV(vg);
        if (predictCII is null)
        {
            return new CIIAndVDto(null, null, null, L[PHMEEEnergyEfficiencyErrorCodes.CannotProvideValidEstimatedCII]);
        }

        // 对应 CII 的等级
        var predictCIIGrade = CIIService.DetermineCIIGrade(predictCII.Value, vesselEeConfig.VesselType,
            vesselEeConfig.DWT, vesselEeConfig.GT, z.Z);

        return new CIIAndVDto(predictCII, predictCIIGrade, vg, null);
    }

    /// <inheritdoc />
    public async Task<Co2EmissionsDto> CurrentYearCumulativeCo2EmissionsAsync(Guid vesselId)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();
        // 本年已经过去的整日数据
        var startDate = QueryDateTimeUtils.GetUtcYearStartDate(utcNow.Year);
        var endDate = _phmeeTimeProvider.GetUtcToday();

        var logAbstracts = await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDate, endDate);

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new Co2EmissionsDto(null);
        }

        var co2Emissions = LogAbstractFormulas.CO2Emissions(logAbstracts, vesselEeConfig.FuelTypes);
        return new Co2EmissionsDto(co2Emissions);
    }

    /// <inheritdoc />
    public async Task<Co2EmissionsDto> CurrentYearCumulativeCo2EmissionsAdjustedAsync(Guid vesselId)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();
        var yearNumber = utcNow.Year;
        // 本年已经过去的整日数据
        var startDate = QueryDateTimeUtils.GetUtcYearStartDate(utcNow.Year);
        var endDate = _phmeeTimeProvider.GetUtcToday();

        var z = await _ciiReductionFactorRepository.GetByYearAsync(yearNumber);
        if (z is null)
        {
            return new Co2EmissionsDto(null);
        }

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new Co2EmissionsDto(null);
        }

        var logAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDate, endDate);

        // 航次
        var voyages = await _logAbstractDomainService.GetVoyagesAsync(logAbstracts, vesselId);

        // 调整 log abstract
        var adjustedLogAbstracts =
            await _logAbstractDomainService.GetAdjustedLogAbstractsByTimeRangeAsync(logAbstracts, vesselId);

        // sts log abstracts
        // var stsLogAbstracts = LogAbstractDomainService.GetStsLogAbstracts(voyages);
        var stsLogAbstracts = voyages.GetSTSLogAbstracts();

        var yi = LogAbstractFormulas.GetYi(yearNumber);

        // 得到结果后单位转为 t
        var co2EmissionsCorrect = LogAbstractFormulas.CO2EmissionsCorrect(logAbstracts, adjustedLogAbstracts,
            stsLogAbstracts, yi,
            vesselEeConfig.FtTanker, vesselEeConfig.FuelTypes) * 1e-6f;

        return new Co2EmissionsDto(co2EmissionsCorrect);
    }

    /// <inheritdoc />
    public async Task<CIIComparisionDto> CIIOperationalCarbonIntensityComparisonAsync(Guid vesselId,
        DateTime startDate, DateTime endDate)
    {
        var (startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForDateRangeQuery(startDate, endDate);

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            throw new UserFriendlyException(L[PHMEEEnergyEfficiencyErrorCodes.VesselConfigNotFound],
                PHMEEEnergyEfficiencyErrorCodes.VesselConfigNotFound);
        }

        var ciiComparisonData =
            await CIIOperationalCarbonIntensityComparisonByDataRangeAsync(vesselId, startDateTime, endDateTime);

        var (minBound, maxBound) = GetCIIComparisonBoundaries(ciiComparisonData, vesselEeConfig.CIIDefaultBound.Min,
            vesselEeConfig.CIIDefaultBound.Max);

        return new CIIComparisionDto
        {
            MinBound = minBound,
            MaxBound = maxBound,
            Data = ciiComparisonData
        };
    }

    /// <inheritdoc />
    public async Task<CIIComparisionDto> CIIOperationalCarbonIntensityComparisonByYearAsync(Guid vesselId, int year)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();
    
        var (_, startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForYearQuery(year, utcNow);

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            throw new UserFriendlyException(L[PHMEEEnergyEfficiencyErrorCodes.VesselConfigNotFound],
                PHMEEEnergyEfficiencyErrorCodes.VesselConfigNotFound);
        }

        var ciiComparisonData =
            await CIIOperationalCarbonIntensityComparisonByDataRangeAsync(vesselId, startDateTime, endDateTime);

        var (minBound, maxBound) = GetCIIComparisonBoundaries(ciiComparisonData, vesselEeConfig.CIIDefaultBound.Min,
            vesselEeConfig.CIIDefaultBound.Max);

        return new CIIComparisionDto
        {
            MinBound = minBound,
            MaxBound = maxBound,
            Data = ciiComparisonData
        };
    }

    /// <summary>
    /// 计算获取 CII 折线图的纵轴边界
    /// </summary>
    /// <param name="ciiComparisonData"></param>
    /// <param name="defaultMinBound"></param>
    /// <param name="defaultMaxBound"></param>
    /// <returns></returns>
    private static (float minCIIBound, float maxCIIBound) GetCIIComparisonBoundaries(
        IReadOnlyList<EEOICIIComparisonTimeSeriesDatum> ciiComparisonData, float defaultMinBound, float defaultMaxBound)
    {
        var minCII = ciiComparisonData.Min(d => d.EEOIOrCII) ?? defaultMinBound;
        var maxCII = ciiComparisonData.Max(d => d.EEOIOrCII) ?? defaultMaxBound;

        var minBound = minCII < defaultMinBound ? minCII : defaultMinBound;
        var maxBound = maxCII > defaultMaxBound ? maxCII + 1f : defaultMaxBound;

        return (minBound, maxBound);
    }

    /// <summary>
    /// 按照日期段获取 CII 折线图的数据
    /// </summary>
    /// <param name="vesselId"></param>
    /// <param name="startDate"></param>
    /// <param name="endDate"></param>
    /// <returns></returns>
    private async Task<List<EEOICIIComparisonTimeSeriesDatum>> CIIOperationalCarbonIntensityComparisonByDataRangeAsync(
        Guid vesselId, DateTime startDate, DateTime endDate)
    {
        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig == null)
        {
            return new List<EEOICIIComparisonTimeSeriesDatum>();
        }

        var capacity = VesselEeConfigDomainService.GetCapacity(vesselEeConfig.VesselType, vesselEeConfig.DWT,
            vesselEeConfig.GT);

        var logAbstracts = await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDate, endDate);
        var groupedLogAbstracts = logAbstracts.GroupBy(la => QueryDateTimeUtils.GetOpenCloseUtcDate(la.Ts))
            .ToDictionary(g => g.Key, g => g.ToList());

        List<EEOICIIComparisonTimeSeriesDatum> timeSeriesData = [];

        // 我们只取 EndDate 0 点的数据，而前面 groupedLogAbstracts 时已经通过 CBMDateTimeHelper.GetOpenCloseUtcDate 方法将 0 点的数据算到前一天了
        // 所以这里 date < endDate 即可
        for (var date = startDate; date < endDate; date = date.AddDays(1))
        {
            if (groupedLogAbstracts.TryGetValue(date, out var logAbstractsForDate))
            {
                var cii = LogAbstractFormulas.CIINullable(logAbstractsForDate, capacity, vesselEeConfig.FuelTypes);
                timeSeriesData.Add(new EEOICIIComparisonTimeSeriesDatum(date, cii, false));
            }
            else
            {
                // IsNoData 置为 true，说明是没有数据导致结果为 null
                timeSeriesData.Add(new EEOICIIComparisonTimeSeriesDatum(date, null, true));
            }
        }

        return timeSeriesData;
    }

    /// <inheritdoc />
    public async Task<CIIGradeBoundariesDto> CIIGradeBoundariesAsync(Guid vesselId)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new CIIGradeBoundariesDto(null);
        }

        var z = await _ciiReductionFactorRepository.GetByYearAsync(utcNow.Year);
        if (z is null)
        {
            return new CIIGradeBoundariesDto(null);
        }

        // 要求的年度运营 CII
        var requiredCII =
            CIIService.RequiredCII(vesselEeConfig.VesselType, vesselEeConfig.DWT, vesselEeConfig.GT, z.Z);

        // CII 等级边界
        var boundaries = CIIService.GetAllGradeBoundaries(requiredCII, vesselEeConfig.VesselType, vesselEeConfig.DWT);

        return new CIIGradeBoundariesDto(boundaries);
    }
}